using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;
using TemplatesDatabase;

namespace SCIENEW {
    public class ComponentFluidTank : ComponentTankBase, IUpdateable, IDrawable {
        public static float UnitVolume = 64f;

        public SubsystemTerrain m_subsystemTerrain;

        public ComponentBlockEntity m_componentBlockEntity;

        public PrimitivesRenderer3D m_primitivesRenderer = new();

        public Fluid m_containsFluid;

        public TexturedBatch3D m_batch;

        public DrawBlockEnvironmentData m_drawBlockEnvironmentData = new();

        public int m_lastFluidID = -1;

        public UpdateOrder UpdateOrder => UpdateOrder.Default;

        public int[] DrawOrders => [1];

        public float Capacity {
            get => m_tanks[0].Capacity;
            set => m_tanks[0].Capacity = value;
        }

        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap) {
            base.Load(valuesDictionary, idToEntityMap);
            m_subsystemTerrain = Project.FindSubsystem<SubsystemTerrain>(true);
            m_componentBlockEntity = Entity.FindComponent<ComponentBlockEntity>(true);
            Capacity = 0;
        }

        public void Update(float dt) {
            if (Capacity <= 0) {
                UpdateVolume(false);
            }
            else if(m_tanks[0].Volume > Capacity) {
                m_tanks[0].Volume = Capacity;
                m_containsFluid?.Emit(Project, new Vector3(m_componentBlockEntity.Coordinates), Vector3.Zero);
            }
            if (m_tanks[0].Value != m_lastFluidID) {
                m_lastFluidID = m_tanks[0].Value;
                OnSwitchFluid();
            }
        }

        public void OnSwitchFluid() {
            m_containsFluid = FluidsManager.GetFluid(m_tanks[0].Value);
            m_batch = m_primitivesRenderer.TexturedBatch(m_containsFluid.GetTexture(m_tanks[0].Value), samplerState: SamplerState.PointWrap);
        }

        public void UpdateVolume(bool isFast = true) {
            int tanksNum = 0;
            Point3 point = m_componentBlockEntity.Coordinates;
            while (IsTank(point, isFast)) {//往上搜索有多少个储罐连在一起了
                point += Point3.UnitY;
                tanksNum++;
            }
            m_tanks[0].Capacity = UnitVolume * tanksNum;
        }

        bool IsTank(Point3 point, bool isFast = true) {
            int cellValue = isFast ? m_subsystemTerrain.Terrain.GetCellValueFast(point.X, point.Y, point.Z) : m_subsystemTerrain.Terrain.GetCellValue(point.X, point.Y, point.Z);
            int cellID = Terrain.ExtractContents(cellValue);
            if (cellID == BaseDeviceBlock.Index
                && DevicesBlockManager.GetDevice(cellValue) is FluidTankDevice) {
                return true;
            }
            return false;
        }

        public void Draw(Camera camera, int drawOrder) {
            if (!DrawingUtils.IsVisible(m_subsystemTerrain, camera, new Vector3(m_componentBlockEntity.Coordinates))) return;

            if (m_containsFluid != null && m_tanks[0].Volume > 0f) {
                //绘制位置参数
                DrawingUtils.ModifyEnvironmentData(m_drawBlockEnvironmentData, m_subsystemTerrain, m_componentBlockEntity.Coordinates);
                float bottomHeight = 0.02f;
                float topHeight = ((Capacity - UnitVolume) / UnitVolume) * 1 + 0.98f;
                float fluidHeight = MathUtils.Lerp(bottomHeight, topHeight, m_tanks[0].Volume / Capacity);
                Vector3 oriPos = new(m_componentBlockEntity.Coordinates);
                Vector3 b1 = oriPos + new Vector3(1.2f / 16f, bottomHeight, 1.2f / 16f);
                Vector3 b2 = oriPos + new Vector3(1.2f / 16f, bottomHeight, 14.8f / 16f);
                Vector3 b3 = oriPos + new Vector3(14.8f / 16f, bottomHeight, 14.8f / 16f);
                Vector3 b4 = oriPos + new Vector3(14.8f / 16f, bottomHeight, 1.2f / 16f);
                Vector3 t1 = oriPos + new Vector3(1.2f / 16f, fluidHeight, 1.2f / 16f);
                Vector3 t2 = oriPos + new Vector3(1.2f / 16f, fluidHeight, 14.8f / 16f);
                Vector3 t3 = oriPos + new Vector3(14.8f / 16f, fluidHeight, 14.8f / 16f);
                Vector3 t4 = oriPos + new Vector3(14.8f / 16f, fluidHeight, 1.2f / 16f);
                //材质参数
                int fluidValue = m_tanks[0].Value;
                float waterSlotsWidth = 1f / m_containsFluid.GetTextureSubdivision(fluidValue); //材质水格子的宽度
                int texSlotIndex = m_containsFluid.GetTextureSlot(fluidValue);
                Vector2 texPosition = new(waterSlotsWidth * (texSlotIndex % m_containsFluid.GetTextureSubdivision(fluidValue)), waterSlotsWidth * (texSlotIndex / m_containsFluid.GetTextureSubdivision(fluidValue)));
                Vector2 tp1 = texPosition;
                Vector2 tp2 = texPosition + Vector2.UnitX * waterSlotsWidth;
                Vector2 tp3 = texPosition + Vector2.One * waterSlotsWidth;
                Vector2 tp4 = texPosition + Vector2.UnitY * waterSlotsWidth;
                float hDw = (fluidHeight - bottomHeight) / (14.8f / 16f - 1.2f / 16f);//侧面的高宽比
                Vector2 tp3_c = texPosition + new Vector2(waterSlotsWidth, waterSlotsWidth * hDw);
                Vector2 tp4_c = texPosition + new Vector2(0, waterSlotsWidth * hDw);
                float s = LightingManager.LightIntensityByLightValue[m_drawBlockEnvironmentData.Light];
                Color color = m_containsFluid.IsGlow(fluidValue) ? m_containsFluid.GetColor(fluidValue) : Color.MultiplyColorOnly(m_containsFluid.GetColor(fluidValue), s);
                //四面
                Vector3 normal = Vector3.Normalize(Vector3.Cross(b2 - b1, t1 - b1));
                m_batch.QueueQuad(b1, b2, t2, t1, tp1, tp2, tp3_c, tp4_c, Color.MultiplyColorOnly(color, LightingManager.CalculateLighting(normal)));
                normal = Vector3.Normalize(Vector3.Cross(b3 - b2, t2 - b2));
                m_batch.QueueQuad(b2, b3, t3, t2, tp1, tp2, tp3_c, tp4_c, Color.MultiplyColorOnly(color, LightingManager.CalculateLighting(normal)));
                normal = Vector3.Normalize(Vector3.Cross(b4 - b3, t3 - b3));
                m_batch.QueueQuad(b3, b4, t4, t3, tp1, tp2, tp3_c, tp4_c, Color.MultiplyColorOnly(color, LightingManager.CalculateLighting(normal)));
                normal = Vector3.Normalize(Vector3.Cross(b1 - b4, t4 - b4));
                m_batch.QueueQuad(b4, b1, t1, t4, tp1, tp2, tp3_c, tp4_c, Color.MultiplyColorOnly(color, LightingManager.CalculateLighting(normal)));
                //底面
                m_batch.QueueQuad(b1, b2, b3, b4, tp1, tp2, tp3, tp4, Color.MultiplyColorOnly(color, LightingManager.CalculateLighting(-Vector3.UnitY)));
                //顶面
                m_batch.QueueQuad(t1, t2, t3, t4, tp1, tp2, tp3, tp4, Color.MultiplyColorOnly(color, LightingManager.CalculateLighting(Vector3.UnitY)));
            }

            m_primitivesRenderer.Flush(camera.ViewProjectionMatrix);
        }
    }
}